/*
 *  AVM.cpp
 *  OfflineTranslator
 *
 *  Created by PH on 5.9.10.
 *  Copyright 2010 Petr Homola. All rights reserved.
 *
 */

#include "global.h"
#include "stdafx.h"
#include "AVM.h"
#include <algorithm>

namespace nlp {
	
	//int avmAllocs = 0;
	
	void unrollVector(const vector<vector<string> >* v, int pos, const string& prefix, BLOCKVAR(block, void, const string&)) {
		const vector<string>& v2 = (*v)[pos];
		for (int i = 0; i < v2.size(); i++) {
			const string& s = v2[i];
			string newPrefix = (prefix.size() > 0 ? prefix + " " : "") + s;
			if (pos + 1 < v->size()) unrollVector(v, pos + 1, newPrefix, block);
			else block(newPrefix);
		}
		
	}
	
	void unrollVector(const vector<vector<string> >* v, BLOCKVAR(block,void, const string&)) {
		unrollVector(v, 0, "", block);
	}

	void unrollVector(const vector<vector<string> >* v1, const vector<vector<string> >* v2, BLOCKVAR(block, void, const string&, const string&)) {
		unrollVector(v1, 0, "", BLOCK(=)(const string& prefix) {
			unrollVector(v2, 0, "", BLOCK(=)(const string& suffix) {
				block(prefix, suffix);
			});
		});
	}
	
	void addToAffixes(vector<vector<string> >& v, shared_ptr<AVM> _avm) {
		vector<string> _list;
		vector<string>* list = &_list;
		_avm->linearize(BLOCK(list)(const string& result) {
			list->push_back(result);
		}, true);
		v.push_back(_list);
	}
	
	bool AVM::equals(const AVM& _avm) const {
		__block vector<string> keys1;
		__block vector<string> keys2;
		enumerateKeysAndValues(BLOCK(&keys1)(const string& key, const Value& value) {
			keys1.push_back(key);
		});
		_avm.enumerateKeysAndValues(BLOCK(&keys2)(const string& key, const Value& value) {
			keys2.push_back(key);
		});
		if (keys1.size() == keys2.size()) {
			sort(keys1.begin(), keys1.end());
			sort(keys2.begin(), keys2.end());
			for (int i = 0; i < keys1.size(); i++) {
				const string& key1 = keys1[i];
				const string& key2 = keys2[i];
				if (key1 != key2) {
					//cout << "##### " << key1 << " " << key2 << endl;
					return false;
				}
				if (!get(key1).equals(_avm.get(key2))) return false;
			}
			return true;
		}
		return false;
	}
		
	void AVM::linearize(BLOCKVAR(block, void, const string&), bool withAlternatives) const {
		vector<vector<string> >* prefixes = new vector<vector<string> >;
		//__block vector<string>* list;
		//__block BLOCKVAR(block2, void, const string&);
		enumerateLeftChildrenSorted(BLOCK(prefixes)(const string& key, const shared_ptr<AVM>& _avm) {
			if (_avm->get("silent").description() != "1") {
				addToAffixes(*prefixes, _avm);
			}
		}, false);
		
		vector<vector<string> >* suffixes = new vector<vector<string> >;
		enumerateRightChildrenSorted(BLOCK(prefixes, suffixes)(const string& key, const shared_ptr<AVM>& _avm) {
			if (_avm->get("silent").description() != "1") {
				addToAffixes(*suffixes, _avm);
			}
		}, false);
		
		const string& form = getForm();
		if (prefixes->size() > 0) {
			if (suffixes->size() > 0) {
				unrollVector(prefixes, suffixes, BLOCK(block, form)(const string& prefix, const string& suffix) {
					block(prefix + " " + form + " " + suffix);
				});
			} else {
				unrollVector(prefixes, BLOCK(block, form)(const string& prefix) {
					block(prefix + " " + form);
				});
			}
		} else {
			if (suffixes->size() > 0) {
				unrollVector(suffixes, BLOCK(block, form)(const string& suffix) {
					block(form + " " + suffix);
				});
			} else block(form);
		}
		delete prefixes;
		delete suffixes;
		
		if (withAlternatives && alternativeAvm != NULL) {
			//cout << "has alternatives: " << get("lemma").description() << endl;
			alternativeAvm->linearize(block, true);
		}
	}
		
}
